JavaScript Fetch Todo List Practice
This exercise uses the JSONPlaceholder test API to practise requesting todo items and displaying them in the browser.
The goal is to send a GET request with fetch(), validate the HTTP response, convert the response body from JSON and render each task with its completion state.
What you are practising
- sending a GET request to a public API;
- checking
response.okbefore using a response; - reading JSON data with
response.json(); - destructuring the
id,titleandcompletedproperties; - creating markup with
map()and combining it withjoin(""); - showing loading, success and error messages.
Request flow
Browser
-> fetch(url)
-> API response
-> check response.ok
-> response.json()
-> create todo markup
-> render the list
Working example
The query parameter _limit=10 keeps the example concise. The checkboxes are disabled because they display API data; this lesson does not send updates back to the server.
Loading todo items…
JavaScript
const todoList = document.querySelector(".todo-list");
const status = document.querySelector(".todo-demo-status");
fetch("https://jsonplaceholder.typicode.com/todos?_limit=10")
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then((todos) => {
todoList.innerHTML = createMarkup(todos);
status.textContent = `Loaded ${todos.length} todo items.`;
})
.catch((error) => {
status.textContent = `Could not load todo items: ${error.message}`;
});
function createMarkup(todos) {
return todos
.map(({ id, title, completed }) => `
<li class="todo-list-item" data-id="${id}">
<input
type="checkbox"
aria-label="${title}"
${completed ? "checked" : ""}
disabled
>
<p>${title}</p>
</li>
`)
.join("");
}
How the code works
fetch() starts the request and returns a promise. A fulfilled promise does not automatically mean that the server returned a successful status, so the code checks response.ok.
response.json() reads the response body and returns another promise containing JavaScript data. The next then() receives the todo array.
Inside createMarkup(), destructuring extracts the three properties needed by the interface. map() creates one list-item string for every todo, while join("") combines those strings without commas.
The conditional expression adds checked only when completed is true. The final catch() reports network, response or data-processing errors to the visitor.
Practice tasks
- Change
_limit=10to another number and compare the result. - Use
filter()to display only completed todo items. - Add the todo
idbefore each title. - Rewrite the request using
asyncandawait. - Add a button that reloads the data after an error.